package data;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.Vector;
import java.util.Map.Entry;

public class SubForum {
	private Integer _id;
	private String _topic;
	private HashMap<Integer,Member> _moderatorCollection;
	private HashMap<Integer,ThreadMessage> _messages;
	private Forum _Forum;
	private Integer _messageIndex;
	private Vector<Member> _bannedUsers;
	private HashMap<String,Integer> _MapUserToNumMessages;
	
	public SubForum(Integer id, String topic, Forum Forum, Member moderator) {
		super();
		_id = id;
		_topic = topic;
		_Forum = Forum;
		_messages= new HashMap<Integer, ThreadMessage>();
		_moderatorCollection= new HashMap<Integer, Member>();
		_moderatorCollection.put(moderator.get_id(), moderator);
		_messageIndex=1;
		_bannedUsers=new Vector<Member>();
		_MapUserToNumMessages= new HashMap<String, Integer>();
	}
	
	public SubForum(Integer id, String topic, Forum Forum, HashMap<Integer, Member> moderators) {
		super();
		_id = id;
		_topic = topic;
		_Forum = Forum;
		_messages= new HashMap<Integer, ThreadMessage>();
		_moderatorCollection= moderators;
		_messageIndex=1;
		_bannedUsers=new Vector<Member>();
		_MapUserToNumMessages=new HashMap<String, Integer>();
	}
	
	public ThreadMessage getThreadMessage(Integer Index){
		return this._messages.get(Index);
	}

	public Integer get_id() {
		return _id;
	}
	public String get_topic() {
		return _topic;
	}
	public HashMap<Integer, Member> get_moderatorCollection() {
		return _moderatorCollection;
	}
	public Map<Integer, ThreadMessage> get_messages() {
		return _messages;
	}
	public Forum get_Forum() {
		return _Forum;
	}
	
	public Vector<Member> get_bannedUsers() {
		return _bannedUsers;
	}

	public HashMap<String, Integer> get__MapUserToNumMessages() {
		return _MapUserToNumMessages;
	}

	public void set_topic(String topic) {
		_topic = topic;
	}
	
	public synchronized Integer get_messageIndex() {
		Integer ans = new Integer(_messageIndex);
		_messageIndex++;
		return ans;
	}
	/**
	 * method which add new thread Message. this method updates member. DO NOT update member again.
	 * @param title title of msg
	 * @param content body of msg
	 * @param composer composer of msg
	 * @param date time stamp of publish
	 */
	public void addThreadMsg(String title, String content, Member composer ,Date date){
		Integer msgIndex = this.get_messageIndex();
		ThreadMessage tThreadmsg = new ThreadMessage(msgIndex, title, content, composer, date, get_id());
		this._messages.put(msgIndex, tThreadmsg);
		composer.addMessage(this.get_id(), msgIndex);
		composer.set_lastMsgPublish(date);
		increaseNumMsgForUser(composer.get_name());
	}
/**
 *  method which add new post msg. this method updates member. DO NOT update member again.
 * @param title title of msg
 * @param content body of msg
 * @param composer composer of msg
 * @param date time stamp of publish
 * @param threadMsgIndex thread msg which the post refers to
 * @param responseMsgIndex msg id which is responded to
 * @return true in case of success , false otherwise
 */	
	public boolean addPostMsg(String title, String content, Member composer ,Date date,Integer threadMsgIndex,Integer responseMsgIndex)
	{
		Integer msgIndex = this.get_messageIndex();
		ThreadMessage threadMsg = this._messages.get(threadMsgIndex);
		if (threadMsg == null)
			return false;
		ForumMessage responseTo;
		if (threadMsgIndex.equals(responseMsgIndex))
			responseTo = threadMsg;
		else
			responseTo = threadMsg.getPostedMsg(responseMsgIndex);
		if (responseTo == null)
			return false;
		PostMessage addedPostMsg = new PostMessage(msgIndex, title, content, composer, date, _id, threadMsg, responseTo);
		threadMsg.addNewResponse(addedPostMsg);
		composer.addMessage(this.get_id(), msgIndex);
		composer.set_lastMsgPublish(date);
		increaseNumMsgForUser(composer.get_name());
		System.out.println("num of msgs for user: "+composer.get_name()+" is: "+_MapUserToNumMessages.get(composer.get_name()));
		return true;
	}
	/**
	 * increase number of published msgs to user
	 * @param uName user name
	 */
	private synchronized void increaseNumMsgForUser(String uName) {
		if(this._MapUserToNumMessages.containsKey(uName))
				this._MapUserToNumMessages.put(uName,this._MapUserToNumMessages.get(uName)+1);
		else
			this._MapUserToNumMessages.put(uName,1);
	}
	
/**
 * method which check if user has premission to edit or delete msg
 * @param modifierID modifier id
 * @param composerId msg composer id
 * @return  true in case of success(owner/moderator/admin) , false otherwise
 */
	private boolean isPremitedToEditOrDelete(Integer modifierID,Integer composerId){  
		if (modifierID.equals(composerId))
			return true;
		if(this._Forum.get_admin().get_id().equals(modifierID))
			return true;
		if(_moderatorCollection.containsKey(modifierID)){ 
			// need to add condition which check the period of time moderator is in his position. 
			//more than a month is true.
				Member moderator=_moderatorCollection.get(modifierID);
				Date startModeratorDate = moderator.getStartDateAsModerator(this._id);
				if (checkMoreThenMonth(startModeratorDate))
					return true;
		}
		return false;
	}
	/**
	 * method which check if moderator is more than a month in duty
	 * @param moderatorStartDate start date
	 * @return  true in case of success , false otherwise
	 */
	private boolean checkMoreThenMonth(Date moderatorStartDate){
		Date currentDate = new Date(System.currentTimeMillis());
		long totalTime = (long)currentDate.getTime() - (long)moderatorStartDate.getTime();
		long day =(long) 1000*60*60*24;
		return (totalTime/day) >=30;
	}
	/**
	 * method which edit thread msg
	 * @param userId user id
	 * @param msgId msg id
	 * @param title title of msg
	 * @param content body of msg
	 * @return  true in case of success , false otherwise
	 */
	public boolean updateThreadMessage(Integer userId,Integer msgId, String title,String content){
		if (!this._messages.containsKey(msgId)){
			//System.out.println("msg id incorrect");
			return false;
		}
		ThreadMessage message = this._messages.get(msgId);
		if (!isPremitedToEditOrDelete(userId, message.get_composer().get_id())){
			//System.out.println("user id not premit");
			return false;
		}
		if(title==null || content ==null)
			return false;
		message.set_title(title);
		message.set_content(content);
		return true;
	}
	/**
	 * @param userId user id
	 * @param threadMsgId thread msg ID
	 * @param postMsgId post msg ID
	 * @param title title of msg
	 * @param content body of msg
	 * @return  true in case of success , false otherwise	 
	 */
	public boolean updatePostMessage(Integer userId,Integer threadMsgId,Integer postMsgId, String title,String content){
		if (!this._messages.containsKey(threadMsgId)){
			return false;
		}
		ThreadMessage threadMsg = this._messages.get(threadMsgId);
		if (!threadMsg.containPost(postMsgId)){
			return false;
		}
		PostMessage message = threadMsg.getPostedMsg(postMsgId);
		if (!isPremitedToEditOrDelete(userId, message.get_composer().get_id())){
			return false;
		}
		if(title==null || content ==null)
			return false;
		message.set_title(title);
		message.set_content(content);
		return true;
	}
	/**
	 * method which deletes post in the sub forum. 
	 * @param userId id of userwho asked the operation
	 * @param threadMsgId thread msg ID
	 * @param postMsgId post msg ID
	 * @return true if succeed, false if fails
	 */
	public boolean deletePostMessage(Integer userId,Integer threadMsgId,Integer postMsgId){
		if (!this._messages.containsKey(threadMsgId)){
			return false;
		}
		ThreadMessage threadMsg = this._messages.get(threadMsgId);
		if (!threadMsg.containPost(postMsgId)){
			return false;
		}
		PostMessage message = threadMsg.getPostedMsg(postMsgId);
		if (!isPremitedToEditOrDelete(userId, message.get_composer().get_id())){
			return false;
		}
		threadMsg.deletePostMsg(postMsgId);
		return true;
	}
	/**
	 * method which deletes thread in the sub forum
	 * @param userId user Id 
	 * @param threadMsgId
	 * @return true if succeed, false if fails
	 */
	public boolean deleteThreadMessage(Integer userId,Integer threadMsgId){
		if (!this._messages.containsKey(threadMsgId)){
			return false;
		}
		ThreadMessage threadMsg = this._messages.get(threadMsgId);
		if (!isPremitedToEditOrDelete(userId, threadMsg.get_composer().get_id())){
			return false;
		}
		this._messages.remove(threadMsgId);
		return true;
	}

	/**
	 * method which put member on the ban list of the subforum
	 * @param moderatorId
	 * @param user
	 * @return true if succeed, false if fails
	 */
	public boolean banMember(Integer moderatorId,Member user){
		if (!isPremitedToEditOrDelete(moderatorId, null))
			return false;
		if(!_bannedUsers.contains(user))
			_bannedUsers.add(user);
		return true;
	}
/**
 * method which add moderator to the subforum. this method updates member . DO NOT double update member.
 * @param moderatorUserName member name
 */
	public boolean addModerator(String moderatorUserName) {
		Map<String, Member> users = MemberDataBase.getMembersDataBase().getUsers();
		Member moderator = users.get(moderatorUserName);
		if(moderator!=null){
			this._moderatorCollection.put(moderator.get_id(), moderator);
			moderator.addSubforumAsModerator(get_id());
			return true;
		}
		return false;
	}

	/**
	 * method which edit message in the forum
	 * @param userId id of user
	 * @param messageId id of message
	 * @param threadId thread id
	 * @param newSubject subject of the message
	 * @param newBody body of the message
	 */
	public boolean editMessage(int userId, String messageId, String threadId,
			String newSubject, String newBody) {
		Integer msgID=new Integer(messageId);
		Integer thrdID=new Integer(threadId);
		if (msgID.equals(thrdID))
			return updateThreadMessage(userId, msgID, newSubject, newBody);
		else
			return updatePostMessage(userId, thrdID, msgID, newSubject, newBody);
	}
	/**
	 *  method which remove moderator from sub forum moderators list. this method updates member . DO NOT double update member.
	 * @param userName name of user which asked the operation
	 * @param moderatorName moderator name to be removed
	 * @return true if succeed, false if fails
	 */
	
	public Boolean removeModerator(String userName, String moderatorName) {
		Map<String, Member> users = MemberDataBase.getMembersDataBase().getUsers();
		if (!users.get(userName).get_isAdmin())
			return false;
		Member removedModerator = users.get(moderatorName);
		Integer mID = removedModerator.get_id();
		if (this._moderatorCollection.size() > 1 && this._moderatorCollection.containsKey(mID) && !isPublishMsgInLastHour(removedModerator)){
			this._moderatorCollection.remove(mID);
			removedModerator.removeSubforumAsModerator(get_id());
			return true;
		}
		return false;
	}
	
	/**
	 * method which check if moderator publish msg in the last hour in this sub forum.
	 * @param removedModerator member ID
	 * @return true id member published MSG, false otherwise.
	 */
	private boolean isPublishMsgInLastHour(Member removedModerator) {
		Date currentDate = new Date(System.currentTimeMillis());
		long totalTime = (long)currentDate.getTime() - (long)removedModerator.get_lastMsgPublish().getTime();
		long hour =(long) 1000*60*60;
		return (totalTime/hour) <=1;
	}

	/**
	 * method which replace one moderator by other. this method updates Member. DO NOT double update member.
	 * @param modifierName name of the member who make the replacement (has to be Administrator in order to success).
	 * @param newModerator name of the member which going to be the new moderator of the sub forum.
	 * @param replacedModerator name of the moderator which going to be replaced in the sub forum.
	 * @return true if succeed, false if fails
	 */
	public boolean replaceModerator(String modifierName,String newModerator,String replacedModerator){
		Map<String, Member> users = MemberDataBase.getMembersDataBase().getUsers();
		if (!users.get(modifierName).get_isAdmin())
			return false;
		Member newMod = users.get(newModerator);
		Member replaceMod = users.get(replacedModerator);
		if(!removeModerator(modifierName, replacedModerator))
			return false;
		replaceMod.removeSubforumAsModerator(get_id());
		addModerator(newModerator);
		newMod.addSubforumAsModerator(get_id());
		return true;
	}
	/**
	 * method which return number of msgs for user in sub forum
	 * @param userName user name
	 * @return number of msgs for user in sub forum
	 */
	public Integer getNumberOfMessagesToUser(String userName){
		Integer num= this._MapUserToNumMessages.get(userName);
		if (num==null)
			return 0;
		return num;
	}
	/**
	 * method which return number of msgs in sub forum
	 * @return number of msgs in sub forum
	 */
	public Integer getNumberOfMessagesInSubForum(){
		int counter =0;
		for (Entry<String,Integer> entry : this._MapUserToNumMessages.entrySet()) 
			counter += entry.getValue();
		return counter;
	}
}
